package main;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.directory.InvalidAttributesException;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import jxl.read.biff.BiffException;

import modules.dimensioning.CoverageInterface;
import modules.dimensioning.GeneticSearch;
import modules.dimensioning.LegislationInterface;
import modules.dimensioning.solution.Solution;
import modules.legislation.LegislationAdapter;
import modules.legislation.law.Belgium;
import modules.legislation.law.Brussel;
import modules.legislation.law.CustomLaw;
import modules.legislation.law.Europe;
import modules.legislation.law.LawInterface;
import modules.pathloss.CoverageAdapter;
import modules.pathloss.PathLossAdapter;

import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.FeatureSource;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

import tools.Log;
import tools.gis.antennaReader.AntennaSite;
import tools.gis.antennaReader.AntennaSiteReader;
import tools.gis.shapewriter.Write2Shapefile;
import tools.settings.SettingsAdapter;
import tools.settings.SettingsReaderException;
import tools.settings.gui.SelectEGui;

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;

import datatypes.Antenna;

/**
 * Main startup class om de heuristiek te starten
 * Gui-input zit hier eveneens
 * 
 * @author Bart Moens
 */
public class Main {

    //modules
    SettingsAdapter sa;
    LawInterface law;
    CoverageInterface ci;
    LegislationInterface li;
    GeneticSearch gs;
    //antennes
    Antenna[] antennas;
    Geometry area;

    //settings
    int generations;
    int population;
    int timeInMs;
    String selectedOperator = "";
    File mainFolder;

    public Main(String[] args) throws Exception {

        if (args.length != 1) {
            System.err.println("Usage: Main <absolute path for default folder>");
            System.exit(-1);
        }
        mainFolder = new File(args[0]);
        if (!mainFolder.isDirectory()) {
            System.err.println("Usage: Main <absolute path for default folder>");
            System.exit(-1);
        }

        //load settings from Excel file
        loadExcel();

        //initialises the settings, opens a gui to select the settings file
        loadSettings();

        //inits the coverageAdapter
        startCoverageInterface();

        //this opens a gui to chose your legislation options
        startLegislationInterface();

        //this opens an operator-select box, reads & sorts the sites
        readAntennas();

        //generates the coverage-goal.  This is currently hard-coded for brussels!
        generateCoverageGoal();

        //writes out the current situation
        writeStartConfiguration();

        //runs the genetic search; this opens the options dialog
        runGeneticSearch();

        //retrieve solutions
        SortedSet<Solution> sols = gs.getSolutions();

        //beste solution & wegschrijven:
        Solution best = sols.last();
        writeSolution(best, "BestSol");
        System.out.println("\n\n\nSolution BEST: \n" + best.toString());



    //controleer best & worst met u legislation
    //gaande van 0.1 tot bvb 3V/M
    //en output die naar een shape file (let op de naaaaaaaaammm!!!) :)
/*
        Solution worst = sols.first();
        li.getRadiationExceedingPoints(worst);
        li.writeRadiationPoints2shapefile("worst");
        li.printExceedingRadiationPoints();

        li.setREBEGIN(true);
        li.getRadiationExceedingPoints(best);
        li.writeRadiationPoints2shapefile("best");
        li.printExceedingRadiationPoints();
*/

    }

    private void writeSolution(Solution s, String name) {
        //retrieve output
        //write output
        Write2Shapefile wa2sf = new Write2Shapefile(ci);
        String ss = mainFolder.toString() + "\\" + name + SettingsAdapter.getInstance().getTechnologyName() + "." + selectedOperator + ".Optimized.";
        wa2sf.writeAntennas2shapefile(s.getAntennas(), ss);
    }

    private void runGeneticSearch() throws InvalidAttributesException {
        population = readIntDialog("Insert population size:", 50);
        generations = readIntDialog("Insert generations:", 3000);
        timeInMs = 1000 * readIntDialog("Insert time limit in sec:", 3600);
        Log.add(this, "Population : " + population + ", generations: " + generations + ", time: " + timeInMs + "ms");
        gs = new GeneticSearch(antennas, area, ci, li, population);
        gs.runGeneticAlgorithm(generations, timeInMs);
    }

    private void generateCoverageGoal() throws Exception {
        com.vividsolutions.jts.geom.GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
        WKTReader reader = new WKTReader(geometryFactory);
        //hardcoded for brussels
        //area = reader.read("POLYGON ((90000.00 205000.00, 120000.00 205000.00, 120000.00 180000.00, 90000.00 180000.00, 143630.53714110635 172750.59619320318, 144204.47034874704 174098.09155027266, 145027.93973362286 173923.4162262081, 145302.42952858144 175894.7520263653, 146100.94529573375 175919.70564408883, 146749.73935654497 176793.08226441164, 149644.359012472 177217.2937657113, 149868.9415719836 176269.05629221795, 150542.68925051834 176244.10267449444, 151016.80798726503 177092.52567709374, 151391.11225311767 177217.2937657113, 151416.06587084115 178015.80953286358, 153212.7263469338 178415.06741643974, 153512.1697596159 177891.04144424605, 152663.7467570166 177541.69079611692, 153112.91187603978 176967.7575884762, 152688.70037474012 175869.7984086418, 152963.19016969873 175819.8911731948, 153661.891465957 176867.94311758218, 154260.77829132118 176867.94311758218, 154111.05658498014 176019.52011498285, 154585.1753217268 175395.67967189514, 154610.1289394503 173673.88004897302, 153013.09740514573 173025.08598816177, 152888.3293165282 172451.15278052108, 153786.65955457452 172900.31789954423, 154635.08255717382 172201.61660328598, 155483.50555977313 172401.24554507405, 155508.45917749664 171527.86892475124, 156756.1400636721 171577.77616019826, 156905.86177001314 168383.7130915891, 155558.36641294367 166412.37729143188, 156306.97494464894 165838.44408379117, 156930.81538773666 166038.07302557924, 158452.9860688707 164865.2529925743, 158452.9860688707 164341.2270203806, 157854.0992435065 164291.3197849336, 156406.78941554297 165239.55725842694, 156207.1604737549 165613.86152427958, 155283.87661798505 166187.7947319203, 153686.84508368047 165089.8355520859, 153661.891465957 164341.2270203806, 153836.56679002155 163368.03592916377, 154809.7578812384 163343.08231144026, 155807.90259017874 164241.4125494866, 156531.5575041605 163792.2474304634, 156306.97494464894 163068.59251648164, 155483.50555977313 163268.22145826972, 154061.1493495331 162594.47377973498, 153287.58720010435 163542.71125322834, 152663.7467570166 163941.9691368045, 152838.42208108117 164665.62405078625, 150892.03989864746 165014.97469891538, 150966.900751818 164216.45893176307, 151665.60204807625 164116.64446086905, 151565.78757718223 163318.12869371675, 150991.8543695415 162744.19548607603, 151316.25139994713 161945.67971892375, 150692.4109568594 161521.4682176241, 150318.10669100677 162569.52016201147, 149544.54454157798 162344.9376024999, 149170.24027572534 163168.4069873757, 148371.72450857307 162569.52016201147, 146300.5742375218 164416.08787355115, 146425.34232613936 164615.71681533923, 145651.78017671057 165663.7687597266, 145726.6410298811 166087.98026102624, 144678.58908549373 167011.26411679608, 143480.81543476527 166986.31049907257, 142682.299667613 166562.09899777293, 142432.7634903779 167086.12496996662, 142707.2532853365 167435.47561809575, 141983.59837135475 167909.5943548424, 142382.8562549309 168583.34203337715, 143106.51116891266 168233.99138524802, 143405.95458159476 168832.87821061225))");
        //area = reader.read("POLYGON ((90000.00 205000.00, 120000.00 205000.00, 120000.00 180000.00, 90000.00 180000.00, 90000.00 205000.00))");
        //area = reader.read("POLYGON ((142469.00 162119.00, 142469.00 177972.00, 157792.00 177972.00, 157792.00 162119.00 ,142469.00 162119.00))");
        //area = reader.read("POLYGON ((95047.54518457483 192294.6859605921, 101709.47199445078 201627.97946155688, 113252.41448681007 209279.30134220648, 111240.64450957031 201924.797982789, 110416.14861725894 192228.72628920717, 103061.64525784143 185896.5978362558, 95047.54518457483 192294.6859605921))");
        //area = reader.read("POLYGON (((3.7735618450669293 51.09090600244902, 3.773127050333049 51.053078860601424, 3.7746488319016303 51.053078860601424, 3.7746488319016303 51.04938310536344, 3.772692255599168 51.04938310536344, 3.7724748582322283 51.01090377141502, 3.745952379465521 51.01090377141502, 3.7446479952638803 51.01003418194726, 3.740517445292016 51.011555963515846, 3.739865253191195 51.013729937185246, 3.7070382507832225 51.013947334552185, 3.705951263948521 51.01112116878196, 3.7005163297750157 51.01459952665301, 3.6974727666378526 51.022208434495916, 3.697690164004793 51.05568762900471, 3.6905160508957655 51.055035436903886, 3.681167964117336 51.067861881553355, 3.681602758851217 51.09068860508208, 3.7076904428840427 51.093079976118425, 3.7074730455171028 51.096558333989464, 3.7016033166097166 51.093949565586186, 3.697690164004793 51.097645320824164, 3.7076904428840427 51.10394984446543, 3.75443087677619 51.10308025499767, 3.7735618450669293 51.09090600244902)))");
        /**
         *
         * Flevoland (ruwe omtrek Flevoland Oost en Zuid, i.e. grote eiland zonder Noordoostpolder)
         */
        // hardcoded
        //area = reader.read("POLYGON ((137298.00 487899.00, 171970.00 513617.00, 187418.00 502733.00, 159155.00 473767.00, 137298.00 487899.00))");
        /**
         * To select a shape file
         */
        // first attempt for automatic selection (has to be updated)
        //area =getArea();

        File area_file = promptShapeFile();

        Map<String, Serializable> connectParameters = new HashMap<String, Serializable>();

		try {
			connectParameters.put("url", area_file.toURI().toURL());
			connectParameters.put("create spatial index", true);
			DataStore datastore = DataStoreFinder.getDataStore(connectParameters);

			String[] typeNames = datastore.getTypeNames();
			String typeName = typeNames[0];

			FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = datastore.getFeatureSource(typeName);
			FeatureCollection<SimpleFeatureType, SimpleFeature> collection = featureSource.getFeatures();
			FeatureIterator<SimpleFeature> iterator = collection.features();

			//If not 1: wrong file
			if(collection.size()==1){
				SimpleFeature feature = iterator.next();
				area = (Geometry) feature.getDefaultGeometry();
			}

		} catch (Exception e) {
			e.printStackTrace();
		}


    }

    private void writeStartConfiguration() {
        Write2Shapefile wa2sf = new Write2Shapefile(ci);
        String name = mainFolder.toString() + "\\" + SettingsAdapter.getInstance().getTechnologyName() + "." + selectedOperator + ".Original.";
        wa2sf.writeAntennas2shapefile(antennas, name);
    }

    private void loadExcel() throws FileNotFoundException, IOException, BiffException{
        sa = SettingsAdapter.getInstance();
        sa.setDefaultFolder(mainFolder);
        File file = promptExcelFile();
        try {
            sa.createXML(file);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerConfigurationException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void loadSettings() throws SettingsReaderException, FileNotFoundException {
        sa = SettingsAdapter.getInstance();
        sa.setDefaultFolder(mainFolder);
        File file = promptXmlFile();
        sa.addSettings(new File(sa.getDefault_folder(), file.getName()));
    }

    private void startLegislationInterface() throws InterruptedException {
        SelectEGui sEgui = new SelectEGui();
        while (sEgui.getNotDone())
            Thread.sleep(10);
        if (sEgui.getELimit() != 0)
            law = new CustomLaw(PathLossAdapter.getInstance(), sEgui.getELimit());
        else {
            if (sEgui.getLegislation().equalsIgnoreCase("Brussel"))
                law = new Brussel(PathLossAdapter.getInstance());
            if (sEgui.getLegislation().equalsIgnoreCase("Belgium"))
                law = new Belgium(PathLossAdapter.getInstance());
            else //if(sEgui.getLegislation().equalsIgnoreCase("Europe"))
                law = new Europe(PathLossAdapter.getInstance());
        }
        li = new LegislationAdapter(law, CoverageAdapter.getInstance());
        Log.add(this, "Selected Legislation: " + law.toString());
    }

    private void readAntennas() throws Exception {
        AntennaSiteReader asr = new AntennaSiteReader();
        File f = promptShapeFile();
        List<AntennaSite> list = asr.getAntennaSiteList(f);
        antennas = asr.filterSiteListGUI(list);
        selectedOperator = asr.selectedOperagor;
    }
    private Geometry getArea() throws Exception{
    	File f = promptShapeFileArea();
    	
    	FeatureSource<SimpleFeatureType, SimpleFeature> featureSource;
		FeatureCollection<SimpleFeatureType, SimpleFeature> collection;
		FeatureIterator<SimpleFeature> iterator;
		FileDataStore dataStore = FileDataStoreFinder.getDataStore(f);

		featureSource = dataStore.getFeatureSource();
		collection = featureSource.getFeatures();
		iterator = collection.features();
		
		SimpleFeature feature = iterator.next();
		System.out.println(feature.getAttribute(2));
		Geometry gem = (Geometry) feature.getDefaultGeometry();
    	return gem;
    	
    }

    private void startCoverageInterface() {
        ci = CoverageAdapter.getInstance();
    }
    private File promptExcelFile() throws FileNotFoundException{
        File file;
        JFileChooser chooser = new JFileChooser(mainFolder);
        chooser.setDialogTitle("Open Settings excel file");
        chooser.setFileFilter(new FileFilter() {

            public boolean accept(File f) {
                return f.isDirectory() || f.getPath().endsWith("xls") || f.getPath().endsWith("XLS");
            }

            public String getDescription() {
                return "excel settings file";
            }
        });
        int returnVal = chooser.showOpenDialog(null);

        if (returnVal != JFileChooser.APPROVE_OPTION)
            System.exit(0);
        file = chooser.getSelectedFile();

        Log.add(this, "Excel file: " + file.getName());

        if (!file.exists())
            throw new FileNotFoundException(file.getAbsolutePath());
        return file;
    }

    private File promptXmlFile() throws FileNotFoundException {
        File file;
        JFileChooser chooser = new JFileChooser(mainFolder);
        chooser.setDialogTitle("Open Settings xml file");
        chooser.setFileFilter(new FileFilter() {

            public boolean accept(File f) {
                return f.isDirectory() || f.getPath().endsWith("xml") || f.getPath().endsWith("XML");
            }

            public String getDescription() {
                return "xml settings file";
            }
        });
        int returnVal = chooser.showOpenDialog(null);

        if (returnVal != JFileChooser.APPROVE_OPTION)
            System.exit(0);
        file = chooser.getSelectedFile();

        Log.add(this, "Settingsfile: " + file.getName());

        if (!file.exists())
            throw new FileNotFoundException(file.getAbsolutePath());
        return file;
    }

    private File promptShapeFile()
            throws FileNotFoundException {
        File file;

        JFileChooser chooser = new JFileChooser(mainFolder);
        chooser.setDialogTitle("Open Antenna Shapefile");
        chooser.setFileFilter(new FileFilter() {

            public boolean accept(File f) {
                return f.isDirectory() || f.getPath().endsWith("shp") || f.getPath().endsWith("SHP");
            }

            public String getDescription() {
                return "Shapefiles";
            }
        });
        int returnVal = chooser.showOpenDialog(null);

        if (returnVal != JFileChooser.APPROVE_OPTION)
            System.exit(0);
        file = chooser.getSelectedFile();

        Log.add(new AntennaSiteReader(), "Reading Antennafile: " + file.getName());

        if (!file.exists())
            throw new FileNotFoundException(file.getAbsolutePath());
        return file;
    }
    private File promptShapeFileArea()
    		throws FileNotFoundException {
    	File file;

    	JFileChooser chooser = new JFileChooser(mainFolder);
    	chooser.setDialogTitle("Open Area polygon");
    	chooser.setFileFilter(new FileFilter() {

    		public boolean accept(File f) {
    			return f.isDirectory() || f.getPath().endsWith("shp") || f.getPath().endsWith("SHP");
    		}

    		public String getDescription() {
    			return "Shapefiles";
    		}
    	});
    	int returnVal = chooser.showOpenDialog(null);

    	if (returnVal != JFileChooser.APPROVE_OPTION)
    		System.exit(0);
    	file = chooser.getSelectedFile();

    	Log.add(new AntennaSiteReader(), "Reading Area polygon: " + file.getName());

    	if (!file.exists())
    		throw new FileNotFoundException(file.getAbsolutePath());
    	return file;
    }

    private int readIntDialog(String text, int def) {
        try {
            String s = (String) JOptionPane.showInputDialog(
                    null,
                    text + "\n",
                    "Input integer",
                    JOptionPane.PLAIN_MESSAGE,
                    null,
                    null,
                    def);
            return Integer.parseInt(s);
        } catch (Exception e) {
            return def;
        }

    }

    public static void main(String[] args) {
        try {
            new Main(args);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
